package com.alingrad.android.froyoengine.util;

import java.util.Collection;

import com.alingrad.android.froyoengine.AllocationGuard;

/**
 * Wrapper for a basic array. This is not resizable and will not re-allocate unexpectedly.
 * @author Alan Guo
 *
 * @param <E>
 */
public class FixedSizeArray<E> extends AllocationGuard  {
	private static final int DEFAULT_CAPACITY = 32;
	private E[] mData;
	private int mSize;
	private int mCapacity;

	public FixedSizeArray() {
		this(DEFAULT_CAPACITY);
	}
	
	@SuppressWarnings("unchecked")
	public FixedSizeArray(int capacity) {
		mCapacity = capacity;
		mData = (E[])new Object[capacity];
		mSize = 0;
	}
	
	public boolean add(E object) {
		if (mSize < mCapacity) {
			mData[mSize++] = object;
			return true;
		}
		throw new ArraySizeFullException(mSize);
	}

	public void add(int location, E object) {
		if (mSize < mCapacity) {
			for (int i = mSize++; i > location; i--) {
				mData[i] = mData[i - 1];
			}
			mData[location] = object;
			return;
		}
		throw new ArraySizeFullException(mSize);
	}

	public boolean addAll(Collection<? extends E> list) {
		if (mSize + list.size() <= mCapacity) {
			for (E object : list) {
				mData[mSize++] = object;
			}
			return true;
		}
		throw new ArraySizeFullException(mSize);
	}

	public boolean addAll(int location, Collection<? extends E> list) {
		if (mSize + list.size() <= mCapacity) {
			final int size = list.size();
			for (int i = mSize + size; i >= location + size; i--) {
				mData[i] = mData[i - size];
			}
			int i = location;
			for (E object : list) {
				mData[i] = object;
				i++;
			}
			mSize += size;
			return true;
		}
		throw new ArraySizeFullException(mSize);
	}

	public void clear() {
		mSize = 0;
	}

	public boolean contains(Object object) {
		for (int i = 0; i < mSize; i++) {
			if (mData[i].equals(object))
				return true;
		}
		return false;
	}

	public boolean containsAll(Collection<?> list) {
		for (Object object : list) {
			boolean contained = false;
			for (int i = 0; i < mSize; i++) {
				if (mData[i].equals(object))
					contained = true;
			}
			if (!contained)
				return false;
		}
		return true;
	}

	public E get(int location) 
	{
		if (location < 0 || location >= mSize)
			throw new ArrayIndexOutOfBoundsException(location);
		return mData[location];
	}

	public int indexOf(Object object) {
		for (int i = 0; i < mSize; i++)
			if (mData[i].equals(object))
				return i;
		return -1;
	}

	public boolean isEmpty() {
		return mSize == 0;
	}

	public int lastIndexOf(Object object) {
		for (int i = mSize - 1; i >= 0; i--)
			if (mData[i].equals(object))
				return i;
		return -1;
	}

	public E remove(int location) {
		if (location < 0 || location >= mSize)
			throw new ArrayIndexOutOfBoundsException(location);
		mSize--;
		E retval = mData[location];
		for (int i = location; i < mSize; i++) {
			mData[i] = mData[i + 1];
		}
		return retval;
	}
	
	public E removeLast() {
		if (mSize == 0) 
			throw new ArrayIndexOutOfBoundsException(-1);
		return mData[--mSize];
	}

	public boolean remove(Object object) {
		int index = indexOf(object);
		if (index == -1)
			return false;
		remove(index);
		return true;
	}

	public boolean removeAll(Collection<?> list) {
		boolean retval = false;
		for (Object object : list) {
			if (remove(object))
				retval = true;
		}
		return retval;
	}

	public E set(int location, E object) {
		if (location < 0 || location >= mSize)
			throw new ArrayIndexOutOfBoundsException(location);
		E retval = mData[location];
		mData[location] = object;
		return retval;
	}

	public int size() {
		return mSize;
	}
	
	public int capacity() {
		return mCapacity;
	}

	public Object[] toArray() {
		Object[] retval = new Object[mSize];
		for (int i = 0; i < mSize; i++) 
			retval[i] = mData[i];
		return retval;
	}

	@SuppressWarnings("unchecked")
	public <T> T[] toArray(T[] array) {
		for (int i = 0; i < mSize; i++)
			array[i] = (T) mData[i];
		return array;
	}

}
